package com.aerors.DataProcess;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import javax.imageio.ImageIO;

import org.gdal.gdal.Band;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.Driver;
import org.gdal.gdal.gdal;
import org.gdal.gdalconst.gdalconst;
import org.gdal.gdalconst.gdalconstConstants;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.factory.Hints;
import org.geotools.gce.geotiff.GeoTiffFormat;

public class MapboxTerrainBuilder {

	public MapboxTerrainBuilder() {
		super();
		gdal.AllRegister();
	}

	public static void main(String[] args) {
		MapboxTerrainBuilder builder = new MapboxTerrainBuilder();
		builder.dem2terrainGDAL("C:\\data\\dem\\ASTGTM_N33E099Q.tif");
//		builder.dem2terrainMultiThread("C:\\data\\dem\\aaaa.tif");
		
//		Random random = new Random();
//		
//		for(int i=0;i<10;i++) {
//			builder.calcRGB(random.nextInt(5555));
//		}
	}
	
	public void dem2terrainMultiThread(String tifPath) {
		File rasterFile = new File(tifPath);
		AbstractGridFormat format = GridFormatFinder.findFormat(rasterFile);

		Hints hints = new Hints();
		if (format instanceof GeoTiffFormat) {
			hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
		}

		GridCoverage2DReader reader = format.getReader(rasterFile, hints);
		
		try {
			GridCoverage2D coverage = reader.read(null);
			RenderedImage image = coverage.getRenderableImage(0, 1).createDefaultRendering();
			Raster raster = image.getData();

			int height = raster.getHeight();
			int width = raster.getWidth();
			BufferedImage pngImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			
			int threadNum = 20;
			
			int count = width * height;
			int blockSize = count / threadNum;
			List<int[]> taskList = new ArrayList<>(count);
			ExecutorService pool = Executors.newFixedThreadPool(threadNum);
			for (int row = 0; row < height; row++) {
				for (int col = 0; col < width; col++) {
					int[] colrow = new int[] {col,row};
					taskList.add(colrow);
				}
			}
			
			for (int i = 0; i < threadNum; i++) {
				int start = i*blockSize;
				int end = start + blockSize > count ? count : start + blockSize;
				List<int[]> list = taskList.subList(start, end);
				calcTileThread t = new calcTileThread(list, pngImage, raster);
				pool.submit(t);
			}
			
			pool.shutdown();
			
			while(true) {
				if(pool.isTerminated()) {
					System.out.println("All finished");
					break;
				}
			}
			
			ImageIO.write(pngImage, "png", new FileOutputStream(new File("C:\\data\\dem\\test22.png")));
		} catch (IOException e) {
			System.out.println(e.getCause() + ";" + e.getMessage());
		}
	}
	
	public void dem2terrainGDAL(String tifPath) {
		Dataset dataset = gdal.Open(tifPath, gdalconstConstants.GA_ReadOnly);
		if (dataset == null) {
			System.err.println("GDALOpen failed - " + gdal.GetLastErrorNo());
			System.err.println(gdal.GetLastErrorMsg());
		}
		int width = dataset.getRasterXSize();
		int height = dataset.getRasterYSize();

		Band band = dataset.GetRasterBand(1);
		
		int threadNum = 10;
		ExecutorService pool = Executors.newFixedThreadPool(threadNum);
		
		int dstWidth = 500;
		int dstHeight = 500;
		Driver dstMemDriver = gdal.GetDriverByName("MEM");
		Dataset memDs = dstMemDriver.Create("my_Mem", dstWidth, dstHeight, 3, gdalconst.GDT_Int32);
		
		int[] buffer = new int[dstWidth * dstHeight];
		band.ReadRaster(20, 20, dstWidth, dstHeight, buffer);
//		int[] result= this.process(buffer);
		
		List<Integer> resultList = new ArrayList<>(dstWidth * dstHeight * 3);
//		int[] rList = new int[buffer.length];
//		int[] gList = new int[buffer.length];
//		int[] bList = new int[buffer.length];
		
		//多线程并行计算
		List<Future<List<Integer>>> futureList = new ArrayList<>(threadNum);
		int count = buffer.length;
		int blockSize = count / threadNum;
		List<Integer> taskList = Arrays.stream( buffer ).boxed().collect(Collectors.toList());
		for (int i = 0; i < threadNum; i++) {
			int start = i*blockSize;
			int end = start + blockSize > count ? count : start + blockSize;
			List<Integer> list = taskList.subList(start, end);
			calcTileThread2 t = new calcTileThread2(list);
			Future<List<Integer>> tempFuture = pool.submit(t);
			futureList.add(tempFuture);
		}
		pool.shutdown();
		
		while(true) {
			if(pool.isTerminated()) {
				for(Future<List<Integer>> future : futureList) {
					try {
						resultList.addAll(future.get());
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (ExecutionException e) {
						e.printStackTrace();
					}
				}
				break;
			}else {
				try {
					Thread.sleep(2000);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		int[] arr1 = resultList.stream().mapToInt(Integer::valueOf).toArray();
		
		System.out.println(buffer.length);
		System.out.println(arr1.length);
		memDs.WriteRaster(0, 0, dstWidth, dstHeight, dstWidth, dstHeight, gdalconst.GDT_Int32, arr1, new int[] {1,2,3},  4*3, dstWidth*4*3, 4);
		
		Driver DstDriver = gdal.GetDriverByName("GTiff");
		String dstPath = "C:\\data\\dem\\test444.tif";
		
		DstDriver.CreateCopy(dstPath, memDs);
		
		dataset.delete();
		memDs.delete();
	}
	
	public int[] process(int[] raw) {
		int[] result = new int[raw.length * 3];
		
		int index = 0;
		for(int i =0;i<raw.length;i++) {
			int rawValue = raw[i];
			int[] rgb = this.calcRGB(rawValue);
			result[index] = rgb[0];
			index++;
			result[index] = rgb[1];
			index++;
			result[index] = rgb[2];
			index++;
		}
		
		return result;
	}


	/**
	 * 根据gdal读取出的WKT坐标系信息获取坐标系的WKID
	 * 
	 * @param dataset
	 * @return
	 */
	private String getWKID(Dataset dataset) {
		String crsStr = dataset.GetProjectionRef();
		System.out.println(crsStr);
		String epsgCode = "";
		// 坐标系描述不包含AUTHORITY项
		if (crsStr.indexOf("AUTHORITY") == -1) {
			if (crsStr.indexOf("WGS_1984_Web_Mercator_Auxiliary_Sphere") > -1) {
				epsgCode = "EPSG:3857";
			} else if (crsStr.indexOf("GCS_WGS_1984") > -1) {
				epsgCode = "EPSG:4326";
			}
		} else {
			epsgCode = crsStr.substring(crsStr.lastIndexOf("EPSG"), crsStr.length()).replace(",", ":").replace("]", "")
					.replace("\"", "");
		}
		return epsgCode;
	}
	
	public int[] calcRGB(float elevation) {
		long start = System.currentTimeMillis();
		int[] rgb = new int[3];
		for (int r = 0; r < 256; r++) {
			for (int g = 0; g < 256; g++) {
				for (int b = 0; b < 256; b++) {
					if ((r * 256 * 256 + g * 256 + b) == (elevation + 10000) * 10) {
						rgb[0] = r;
						rgb[1] = g;
						rgb[2] = b;
						break;
					}
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("cost:" + (end - start) + "ms");
		return rgb;
	}

}
class calcTileThread2 implements Callable<List<Integer>>{

	private List<Integer> taskList;
	
	public calcTileThread2(List<Integer> taskList) {
		super();
		this.taskList = taskList;
	}
	
	public int[] calcRGB(float elevation) {
		int[] rgb = new int[3];
		for (int r = 0; r < 256; r++) {
			for (int g = 0; g < 256; g++) {
				for (int b = 0; b < 256; b++) {
					if ((r * 256 * 256 + g * 256 + b) == (elevation + 10000) * 10) {
						rgb[0] = r;
						rgb[1] = g;
						rgb[2] = b;
						break;
					}
				}
			}
		}
		return rgb;
	}
	
	@Override
	public List<Integer> call() throws Exception {
		List<Integer> result = new ArrayList<>(taskList.size() * 3);
		for(Integer rawValue : taskList) {
			int[] rgb = this.calcRGB(rawValue);
			result.add(rgb[0]);
			result.add(rgb[1]);
			result.add(rgb[2]);
		}
		return result;
	}
}

class calcTileThread implements Runnable {
	private List<int[]> taskList;
	private BufferedImage pngImage;
	private Raster raster;

	public calcTileThread(List<int[]> taskList, BufferedImage pngImage, Raster raster) {
		super();
		this.taskList = taskList;
		this.pngImage = pngImage;
		this.raster = raster;
	}

	public int[] calcRGB(float elevation) {
		int[] rgb = new int[3];
		for (int r = 0; r < 256; r++) {
			for (int g = 0; g < 256; g++) {
				for (int b = 0; b < 256; b++) {
					if ((r * 256 * 256 + g * 256 + b) == (elevation + 10000) * 10) {
						rgb[0] = r;
						rgb[1] = g;
						rgb[2] = b;
						break;
					}
				}
			}
		}
		return rgb;
	}

	@Override
	public void run() {
		for(int[] colrow : taskList) {
			int elevation = raster.getSample(colrow[0], colrow[1], 0);
			int[] rgb = this.calcRGB(elevation);
			System.out.println("elevation=" + elevation +";r=" + rgb[0] + ";g=" + rgb[1] + ";b=" + rgb[2]);
			Color c = new Color(rgb[0],rgb[1],rgb[2]);
			pngImage.setRGB(colrow[0], colrow[1], c.getRGB());
		}
	}
}
